package cn.itsource.ymcc.service.impl;

import cn.itsource.ymcc.constant.KillConstants;
import cn.itsource.ymcc.constant.RConst;
import cn.itsource.ymcc.domain.KillActivity;
import cn.itsource.ymcc.domain.KillCourse;
import cn.itsource.ymcc.dto.PreOrderDto;
import cn.itsource.ymcc.mapper.KillActivityMapper;
import cn.itsource.ymcc.mapper.KillCourseMapper;
import cn.itsource.ymcc.result.JsonResult;
import cn.itsource.ymcc.service.IKillCourseService;
import cn.itsource.ymcc.util.AssertUtil;
import cn.itsource.ymcc.util.CodeGenerateUtils;
import cn.itsource.ymcc.util.StrUtils;
import cn.itsource.ymcc.util.UserContext;
import cn.itsource.ymcc.vo.KillVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.w3c.dom.ranges.Range;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zongchangjiang
 * @since 2023-07-12
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {
    @Autowired
    private KillActivityMapper killActivityMapper;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public boolean save(KillCourse entity) {
        KillCourse killCourse = baseMapper.selectOne(new LambdaQueryWrapper<KillCourse>()
                .eq(KillCourse::getCourseName, entity.getCourseName())
                .eq(KillCourse::getActivityId, entity.getActivityId()));
        AssertUtil.isNull(killCourse,"该课程已经加入当前活动");
        entity.setCreateTime(new Date());
        KillActivity killActivity = killActivityMapper.selectById(entity.getActivityId());
        entity.setStartTime(killActivity.getBeginTime());
        entity.setEndTime(killActivity.getEndTime());
        entity.setPublishTime(killActivity.getPublishTime());
        entity.setTimeStr(killActivity.getTimeStr());
        return  super.save(entity);
    }

    @Override
    public void publish() {
        Date date = new Date();
        // 将数据库里面的数据同步到 redis里面
        List<KillCourse> killCourses = baseMapper.selectList(new LambdaQueryWrapper<KillCourse>()
                .eq(KillCourse::getPublishStatus, RConst.Num.BASE_LONG_0)//0待发布，1秒杀中，2秒杀结束
                .gt(KillCourse::getEndTime, date)
                .lt(KillCourse::getStartTime, date));
        if(killCourses.isEmpty()){
            return;
        }
        //将查询出来的数据保存到redis里面
        Map<String,KillCourse> mapAll = new HashMap<>();
        for (KillCourse killCours : killCourses) {
            //将某个商品的库存设置到redis里面
            redisTemplate.opsForValue().set(KillConstants.KILL_STORE+killCours.getId(),killCours.getKillCount());
            //生成一个随机验证码 不需
            //killCours.setKillCode(StrUtils.getComplexRandomString(10)); //如果设置了这东西  那么我们在秒杀的就必须要输入验证码
            //添加课程预热
            mapAll.put(killCours.getId().toString(),killCours);
            //修改我们当前这条数据的状态
            killCours.setPublishTime(date);
            killCours.setPublishStatus(RConst.Num.INT_1);
            baseMapper.updateById(killCours);
        }
        //以hash结构去存储  大key + 小key = value
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KillConstants.KEY_KILL_COURSE);
        hashOps.putAll(mapAll);
    }

    @Override
    public JsonResult killList() {
        BoundHashOperations<String, Object, Object> bho = redisTemplate.boundHashOps(KillConstants.KEY_KILL_COURSE);
        List<Object> values = bho.values();
        return JsonResult.success().setData(values);
    }

    @Override
    public JsonResult killDetail(Long id) {
        BoundHashOperations<String, Object, Object> bho = redisTemplate.boundHashOps(KillConstants.KEY_KILL_COURSE);
        Object o = bho.get(id.toString());
        return JsonResult.success().setData(o);
    }

    /**
     * 接受那些参数
     *    用户id
     *    秒杀的课程id
     *
     * @param killVo
     * @return
     */
    @Override
    public JsonResult kill(KillVo killVo,HttpServletRequest request) {
        //得到用户id
        Long userId = UserContext.getUserId(request.getHeader(UserContext.JWT_TOKEN_NAME));
        //获取课程详情
        BoundHashOperations<String, Object, Object> bho = redisTemplate.boundHashOps(KillConstants.KEY_KILL_COURSE);
        KillCourse killCourse = (KillCourse) bho.get(killVo.getId().toString());
        //最重要  在规定时间之内才可以购买
        //秒杀时间是否合法
        Date date = new Date();
        AssertUtil.isTrue(date.after(killCourse.getStartTime()) && date.before(killCourse.getEndTime()), "秒杀活动时间不合法！");
        //判断用户重复购买
        AssertUtil.isFalse(redisTemplate.hasKey(KillConstants.KEY_PREFIX_PRE_ORDER + userId),"请不要重复抢购");
        //验证够买数量
        AssertUtil.isTrue(killVo.getKillCount()<=killCourse.getKillLimit(),"只能购买"+killCourse.getKillLimit()+"个");
        //实现分布式锁
        RSemaphore semaphore = redissonClient.getSemaphore(KillConstants.KILL_STORE + killVo.getId());
        if(semaphore.tryAcquire(killVo.getKillCount())){//得到就执行业务 没有得到就等
            //生成订单号
            String orderNo = CodeGenerateUtils.generateOrderSn(userId);
            //4. 保存“预创订单数据到Redis”   PreOrderDto：需要什么字段？依据是下订单需要哪些字段 就设置哪些字段
            PreOrderDto preOrderDto = new PreOrderDto();
            preOrderDto.setOrderNo(orderNo);
            preOrderDto.setUserId(userId);
            preOrderDto.setTitle("秒杀订单");
            preOrderDto.setOrderType(1);
            preOrderDto.setPrice(killCourse.getKillPrice());
            preOrderDto.setCount(killVo.getKillCount());
            preOrderDto.setCourseId(killCourse.getCourseId());
            preOrderDto.setCourseName(killCourse.getCourseName());
            preOrderDto.setCoursePic(killCourse.getCoursePic());
            preOrderDto.setKillCourseId(killCourse.getId());
            redisTemplate.opsForValue().set(KillConstants.KEY_PREFIX_PRE_ORDER+orderNo,preOrderDto);

            //一个延迟消息
            rocketMQTemplate.syncSend(
                    KillConstants.TOPIC_ORDER_TIMEOUT+":"+KillConstants.TAGS_ORDER_TIMEOUT,
                    MessageBuilder.withPayload(orderNo).build(),
                    3000, 9
            );

            //标识当前用户已经购买成功
            redisTemplate.opsForValue().set(KillConstants.KEY_PREFIX_PRE_ORDER+userId,"");
            return JsonResult.success().setData(orderNo);
        }else {
            return JsonResult.error("抢购失败");
        }
    }
}
